using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;

namespace InfiniTec.DirectoryServices
{
    /// <summary>
    /// This class can be used to translate names between different formats, such as
    /// the NT4 account name, the Active-Directory distinguished name, Security identifier,
    /// etc.
    /// </summary>
    public class Translator
    {
        private Connection _Connection;

        /// <summary>
        /// Initializes a new instance of the <see cref="Translator"/> class.
        /// </summary>
        /// <param name="connection">The connection to use.</param>
        public Translator(Connection connection)
        {
            if (connection == null) throw new ArgumentNullException("connection");
            _Connection = connection;
        }

        /// <summary>
        /// Gets or sets the connection to use.
        /// </summary>
        /// <value>The connection to use.</value>
        public Connection Connection
        {
            [DebuggerStepThrough]
            get { return _Connection; }
            [DebuggerStepThrough]
            set { _Connection = value; }
        }

        private NameFormat _OutputFormat;

        /// <summary>
        /// Gets or sets the output format.
        /// </summary>
        /// <value>The output format.</value>
        [CLSCompliant(false)]
        public NameFormat OutputFormat
        {
            get { return _OutputFormat; }
            set { _OutputFormat = value; }
        }

        private NameFormat _InputFormat;

        /// <summary>
        /// Gets or sets the input format.
        /// </summary>
        /// <value>The input format.</value>
        [CLSCompliant(false)]
        public NameFormat InputFormat
        {
            [DebuggerStepThrough]
            get { return _InputFormat; }
            [DebuggerStepThrough]
            set { _InputFormat = value; }
        }

        private TranslationFlags _TranslationFlags;

        /// <summary>
        /// Gets or sets the translation flags.
        /// </summary>
        /// <value>The translation flags.</value>
        public TranslationFlags TranslationFlags
        {
            [DebuggerStepThrough]
            get { return _TranslationFlags; }
            [DebuggerStepThrough]
            set { _TranslationFlags = value; }
        }

        /// <summary>
        /// Translates the specified names.
        /// </summary>
        /// <param name="names">The names to translate.</param>
        public IEnumerable<TranslationResult> Translate(IEnumerable<string> names)
        {
            List<string> list = new List<string>();
            list.AddRange(names);
            return Translate(list.ToArray());
        }

        /// <summary>
        /// Translates the specified names.
        /// </summary>
        /// <param name="names">The names to translate.</param>
        public IEnumerable<TranslationResult> Translate(params string[] names)
        {
            IntPtr pIndex;
            SafeNativeMethods.DS_NAME_RESULT container;
            SafeNativeMethods.DS_NAME_RESULT_ITEM[] resultItems;

            IntPtr resultPointer = IntPtr.Zero;

            if (names == null) throw new ArgumentNullException("names", "names must not be null");
            if (names.Length == 0)
                throw new ArgumentException("At least one entry must be passed to this method", "names");

            if (OutputFormat == NameFormat.SidOrSidHistory)
                throw new NotSupportedException("The specified nameformat is not supported as the output format");

            RuntimeHelpers.PrepareConstrainedRegions();
            try
            {
                lock (_Connection.NativeConnectionLock)
                {
                    SafeNativeMethods.DsCrackNames(Connection.DirectoryConnection, TranslationFlags,
                                               InputFormat, OutputFormat, (uint) names.Length, names,
                                               out resultPointer);
                }
                container =
                    (SafeNativeMethods.DS_NAME_RESULT)
                    Marshal.PtrToStructure(resultPointer, typeof (SafeNativeMethods.DS_NAME_RESULT));

                //define the array with size to match				
                resultItems = new SafeNativeMethods.DS_NAME_RESULT_ITEM[container.Count];

                //point to our current DS_NAME_RESULT_ITEM structure
                pIndex = container.Items;

                for (int i = 0; i < container.Count; i++)
                {
                    //marshall back the structure
                    resultItems[i] =
                        (SafeNativeMethods.DS_NAME_RESULT_ITEM)
                        Marshal.PtrToStructure(pIndex, typeof (SafeNativeMethods.DS_NAME_RESULT_ITEM));
                    //update the current pointer idx to next structure
                    pIndex = (IntPtr) (pIndex.ToInt32() + Marshal.SizeOf(resultItems[i]));
                }

                for (int i = 0; i < resultItems.Length; i++)
                {
                    yield return
                        new TranslationResult((TranslationStatus) resultItems[i].Status,
                                              (i < names.Length) ? names[i] : null, resultItems[i].Name,
                                              resultItems[i].Domain);
                }
                
            }
            finally
            {
                if (resultPointer != IntPtr.Zero)
                {
                    SafeNativeMethods.DsFreeNameResult(resultPointer);
                }
            }
        }
    }
}